﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Web;
using System.Xml.Linq;
using System.IO;

/*
 * <skins defaultSkin="">
 * -- skins.xml的根元素，一个skins.xml文件表示了文件所在位置的组件的所有皮肤（外观）。
 * -- defaultSkin表示默认的skin名称。
 * -- 属性名不使用default是由于default是C#中的关键字，不方便用dynamic编程。
 * -- 文件名始终是skins.xml，不能使用其他名称。
 * 
 *      <common>
 *      -- 子元素的结构和skin一样，common是可选的。
 *      -- 表示所有皮肤都需要加载的引用文件
 *      
 *          <js file="文件名.js" miniFile="文件名.js" location="" specialBrowser="" /> 
 *          <css file="文件名.css" miniFile="文件名.css" location="" specialBrowser="" />
 *      </common>
 *      
 *      <skin name="" title="" image=""> 
 *      -- skin可以重复，但是name必须唯一,表示该组件下某皮肤的名称, 至少存在一个名称为Default的元素。
 *      -- 至少存在一个skin元素，其name是skins中的defaultSkin属性值。
 *      -- title表示皮肤的可读性标题。
 *      -- image表示皮肤示意图的文件相对于skins.xml所在目录的路径。
 *      
 *          <js file="文件名.js" miniFile="文件名.js" location="" specialBrowser="" /> 
 *          <css file="文件名.css" miniFile="文件名.js" location="" specialBrowser="" />
 *          -- 表示需要在html文档中引入的脚本和样式文件，分别可以有多个，皮肤组件按顺序引入到页面中。
 *          -- file:必选的属性。表示要在页面中引入的文件。
 *          -- minifile:可选的属性，表示存在小尺寸的发布版本。
 *          -- location:可选的属性，指出文件所在的位置，可以使用完全路径（http://...）、基于网站根的路径（/...）、
 *          -- 基于应用程序的路径（~/...）和相对于skins.xml所在位置的路径（.../...）,如果缺省则表示和skins.xml位于同一个文件夹下。
 *          -- specialBrowser：可选的属性，表示仅在特定的浏览器中才加载，支持的值有："ie<=7"。
 *      </skin> 
 * </skins>
*/
namespace Ruandao.MiniFace
{
    using Ruandao.Misc;

    public class Skin
    {
        public string Name { get; set; }
        public string Title { get; set; }
        public string Image { get; set; }

        /// <summary>
        /// 列表的元素类型包括四个属性：Type="js|css"，File=""，MiniFile="", specialBrowser="".
        /// File中是以全路径或绝对路径表示的文件名。
        /// </summary>
        public List<dynamic> ReferenceFiles { get; private set; }

        public Skin()
        {
            this.ReferenceFiles = new List<dynamic>();
        }
    }

    /// <summary>
    /// 获取一个skins.xml文件的内容。
    /// </summary>
    public class Skins
    {

        /// <summary>
        /// 缓存Skins的实例，为了保证一个skins.xml文件只有一个Skins实例。
        /// </summary>
        private static Dictionary<string, Skins> cacheSkinsInstance = new Dictionary<string, Skins>();

        public static Skins GetInstance(string path)
        {
            if (!cacheSkinsInstance.ContainsKey(path))
            {
                cacheSkinsInstance.Add(path, new Skins(path));
            }
            return cacheSkinsInstance["path"];
        }

        private string skinsPath;
        private string DefaultSkin { get; set; }

        /// <summary>
        /// 列表的元素类型包括三个属性：Type="js|css"，File=""，MiniFile=""
        /// </summary>
        public IList<dynamic> CommonFiles { get; set; }

        /// <summary>
        /// (暂不实现)注意：这部分内容是惰性加载的，不是一次性从文件中读出所有的内容。
        /// </summary>
        private Dictionary<string, Skin> allSkins = new Dictionary<string, Skin>();

        /// <summary>
        /// 读取组件（即某个skins.xml文件）中的所有皮肤信息。
        /// </summary>
        /// <param name="path">skins.xml文件，以绝对地址表示，除了文件系统的路径还支持以http打头的网上文件下载。
        /// 注意：和themes.xml文件不同的是，为了和第三方组件兼容，skins.xml文件可以不必位于Rd_Skins下，因为这里的
        /// path参数是skins.xml文件的路径，而不是Rd_Skins的路径。</param>
        private Skins(string path)
        {
            if (path.EndsWith("/")) this.skinsPath = path;
            else this.skinsPath = path + "/";
            var doc = XDocument.Load(this.skinsPath + "skins.xml");
            dynamic skins = doc.Element("skins").AsDynamic();
            this.DefaultSkin = skins.defaultSkin;
            if (skins.common != null)
            {
                this.CommonFiles = ReadFiles(this.skinsPath, skins.common);
            }
            else
            {
                this.CommonFiles = new List<dynamic>();
            }
            if (skins.skin is DynamicXmlNodeList)
            {
                foreach (var skin in skins.skin)
                {
                    Skin sk = new Skin();
                    sk.Name = skin.name;
                    sk.Title = skin.title;
                    sk.Image = skin.image;
                    sk.ReferenceFiles.AddRange( ReadFiles(this.skinsPath, skin) );
                    allSkins.Add(sk.Name, sk);
                }
            }
            else
            {
                Skin sk = new Skin();
                sk.Name = skins.skin.name;
                sk.Title = skins.skin.title;
                sk.Image = skins.skin.image;
                sk.ReferenceFiles.AddRange( ReadFiles(this.skinsPath, skins.skin) );
                allSkins.Add(sk.Name, sk);
            }
        }

        private static List<dynamic> ReadFiles(string path, dynamic skin)
        {
            List<dynamic> list = new List<dynamic>();
            foreach (var fileNode in skin.Nodes)
            {
                if (fileNode.Name == "js" || fileNode.Name == "css")
                {
                    string file = path + fileNode.file;
                    string miniFile = file;
                    if (!string.IsNullOrEmpty(fileNode.miniFile))
                    {
                        miniFile = path + fileNode.miniFile;
                    }
                    if (fileNode.Name == "js")
                    {
                        list.Add(new { Type = "js", File = file, MiniFile = miniFile });
                    }
                    else
                    {
                        list.Add(new { Type = "css", File = file, MiniFile = miniFile });
                    }
                }
            }
            return list;
        }

        public Skin GetSkin(string skinName)
        {
            if (!allSkins.ContainsKey(skinName))
                skinName = DefaultSkin;

            return allSkins[skinName];
            //Skin skin = allSkins[skinName];
            //if ( skin != null ) return skin;

            //return ReadSkinFormXml(skinName);
        }

        public Skin GetSkin()
        {
            return allSkins[DefaultSkin];
        }
    }
}
